{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 第 3 章 让大模型使用各种工具\n",
    "经常使用大模型的你，一定会发现大模型并不能回答所有的问题。比如大模型大概率无法回答：你的同事小明是谁，如果小明不是特别出名的话。\n",
    "\n",
    "本章将通过一个简单的例子，让你快速了解如何让大模型利用外部工具，来回答一些原本无法回答的问题。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 准备工作\n",
    "\n",
    "### 1.1. 安装\n",
    "\n",
    "下载文档代码及安装依赖项\n",
    "```bash\n",
    "git clone https://github.com/AlibabaCloudDocs/llm_learning.git\n",
    "cd llm_learning\n",
    "pip install -r requirements.txt\n",
    "```\n",
    "\n",
    "### 1.2. 账号准备\n",
    "\n",
    "首先，您需要前往 [官网创建 API Key](https://help.aliyun.com/zh/dashscope/developer-reference/activate-dashscope-and-create-an-api-key)。接下来，请获取你的 [DASHSCOPE_API_KEY](https://dashscope.console.aliyun.com/apiKey)\n",
    "\n",
    "####  MacOS or Linux\n",
    "您可以使用以下命令行导入环境变量\n",
    "```bash\n",
    "export DASHSCOPE_API_KEY=\"sk-****\"\n",
    "```\n",
    "\n",
    "#### Windows\n",
    "可以在终端使用[`SET`](https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/set_1)命令设置环境变量\n",
    "```bat\n",
    "set DASHSCOPE_API_KEY=sk-****\n",
    "```\n",
    "或者在[`PowerShell`](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.4)中使用以下命令行配置环境变量 \n",
    "```powershell\n",
    "$Env:DASHSCOPE_API_KEY = \"sk-****\"\n",
    "```\n",
    "\n",
    "#### Jupyter Notebook\n",
    "您可以使用[`os.environ`](https://docs.python.org/3/library/os.html)方法，在代码开头设置临时环境变量。\n",
    "\n",
    "### 1.3. docenv模式\n",
    "\n",
    "将环境变量写入文件\"~/.zshrc\"中：\n",
    "\n",
    "```bash\n",
    "export OPENAI_API_KEY=\"sk-...\"\n",
    "```\n",
    "就可以执行如下命令 ```source ~/.zshrc``` 将这个环境变量绑定到全局shell中。\n",
    "\n",
    "接下来我们将加载这个环境变量到notebook中。执行如下命令\n",
    "\n",
    "**使用Windows，已经通过Windows PowerShell来注册环境变量的同事可以考虑跳过这里**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Python-dotenv could not parse statement starting at line 7\n",
      "Python-dotenv could not parse statement starting at line 8\n",
      "Python-dotenv could not parse statement starting at line 10\n",
      "Python-dotenv could not parse statement starting at line 11\n",
      "Python-dotenv could not parse statement starting at line 16\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "from dotenv import load_dotenv\n",
    "## for MacOS users\n",
    "filePath = os.path.abspath(os.path.expanduser(os.path.expandvars(\"~/.zshrc\")))\n",
    "load_dotenv(filePath)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.快速开始\n",
    "\n",
    "### 2.1.与Qwen-MAX直接对话\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先我们尝试直接询问大模型关于“阿里云灵积服务”是什么的问题，方式如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "对不起，您的问题中提到的“灵积”服务，目前在我的知识库中并未找到相关确切的信息。由于各类服务层出不穷，且可能具有特定行业背景或地域特色，或者可能是新兴、小众的服务品牌或产品名称，因此我无法直接为您提供准确的解释。\n",
      "\n",
      "为了更好地帮助您，能否请您提供更多关于“灵积”的详细信息？比如它所属的行业领域（如科技、金融、教育等）、主要功能、使用场景，或者任何其他有助于描述这项服务的关键信息。如果有官方网站、相关新闻报道、应用下载链接等，也请一并提供，这样我就能更有效地为您查询和解析。\n",
      "\n",
      "如果您是指某个特定的公司、产品或平台，请核实其准确名称，以便我进行更精确的搜索和解答。如果“灵积”是您记忆中的名称但细节不太确定，也可以尝试描述您记得的其主要特点或服务内容，我将尽力根据这些线索为您提供相关信息或相似服务的解读。\n",
      "\n",
      "期待您的进一步反馈，我会竭力帮您了解这个服务的相关情况。\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.llms import Tongyi\n",
    "\n",
    "llm = Tongyi()\n",
    "# 使用更强大的通义千问 max\n",
    "llm.model_name = 'qwen-max'\n",
    "\n",
    "print(llm.invoke('灵积是什么服务'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以看到由于大模型不了解这个服务，所以在创造一个它认为合理的答案。\n",
    "\n",
    "接下来我们来为大模型添加一些知识，让他能正确回答这个问题。\n",
    "\n",
    "\n",
    "### 2.2.基于Langchain调用自定义工具\n",
    "\n",
    "我们首先通过调用langchain来实现自定义工具的调用，然后我们将手动实现langchain的能力，从而让大家很好的理解其中的奥妙。\n",
    "\n",
    "这里我们将使用langchain中的AgentExecutor来实现这个能力：\n",
    "\n",
    "（注意：如果你遇到了 TypeError: Additional kwargs key output_tokens already exists in left dict and value has unsupported type <class 'int'> 这个错误，请参考[github](https://github.com/langchain-ai/langchain/pull/16580)中的说明）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m 需要查询服务查询工具以了解“灵积”是什么服务\n",
      "Action: 服务查询工具\n",
      "Action Input: 灵积\u001b[0m\u001b[36;1m\u001b[1;3mDashScope灵积模型服务建立在“模型即服务”（Model-as-a-Service，MaaS）的理念基础之上，围绕AI各领域模型，通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。\n",
      "DashScope灵积模型服务依托于业界各领域的优质模型，基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅！\u001b[0m\u001b[32;1m\u001b[1;3m 我现在知道“灵积”是阿里云提供的一个基于“模型即服务”理念的模型服务平台，它提供了模型推理、模型微调训练等服务，并整合了业界各领域的优质模型资源，旨在为AI应用开发者提供便捷、高效的模型服务支持。\n",
      "Final Answer: “灵积”是阿里云推出的一项“模型即服务”（MaaS）产品，该服务围绕人工智能（AI）各领域的模型构建，通过标准应用程序接口（API）提供模型推理、模型微调训练等多种功能。它整合并利用了行业内的高质量模型资源，借助阿里云的强大基础设施运行。其主要目标是为AI应用开发者打造一条轻松访问与利用各类模型的通道，助力他们在开发过程中快速实现模型应用与创新。\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "“灵积”是阿里云推出的一项“模型即服务”（MaaS）产品，该服务围绕人工智能（AI）各领域的模型构建，通过标准应用程序接口（API）提供模型推理、模型微调训练等多种功能。它整合并利用了行业内的高质量模型资源，借助阿里云的强大基础设施运行。其主要目标是为AI应用开发者打造一条轻松访问与利用各类模型的通道，助力他们在开发过程中快速实现模型应用与创新。\n"
     ]
    }
   ],
   "source": [
    "from langchain import hub\n",
    "from langchain.agents import AgentExecutor\n",
    "from langchain.agents import create_react_agent\n",
    "from langchain_community.llms import Tongyi\n",
    "from langchain_core.tools import BaseTool\n",
    "\n",
    "model = Tongyi()\n",
    "model.model_name = 'qwen-max'\n",
    "\n",
    "\n",
    "class SearchTool(BaseTool):\n",
    "    \"\"\"服务查询工具\"\"\"\n",
    "\n",
    "    name: str = \"服务查询工具\"\n",
    "    description: str = (\n",
    "        \"当你不确定一个服务是什么，才使用此工具。\"\n",
    "    )\n",
    "\n",
    "    def _run(self, name: str) -> str:\n",
    "        if name == '灵积':\n",
    "            return '''DashScope灵积模型服务建立在“模型即服务”（Model-as-a-Service，MaaS）的理念基础之上，围绕AI各领域模型，通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。\n",
    "DashScope灵积模型服务依托于业界各领域的优质模型，基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅！'''\n",
    "        return name + '抱歉，没有查到相关信息。'\n",
    "\n",
    "\n",
    "tools = [SearchTool()]\n",
    "\n",
    "prompt = hub.pull(\"hwchase17/react\")\n",
    "\n",
    "agent = create_react_agent(model, tools, prompt)\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n",
    "\n",
    "result = agent_executor.invoke({'input': '灵积是什么服务'})\n",
    "print(result['output'])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.原理说明\n",
    "\n",
    "第一次执行时，我们直接向大模型提问：灵积是什么服务。 这时大模型并没有在预训练中学习过关于“灵积”服务的知识，因此无法回答。\n",
    "\n",
    "为了解决这个问题，我们可以借助外部工具，比如搜索引擎工具如Elastic Search等，先查询一下灵积是什么服务，然后再向大模型提问。有了准确的信息补充，大模型就能回答的更准确了。\n",
    "\n",
    "上述代码采用了\"hwchase17/react\"风格，这种prompt风格定义为：\n",
    "```\n",
    "input_variables=['agent_scratchpad', 'input', 'tool_names', 'tools'] \n",
    "template='Answer the following questions as best you can. You have access to the following tools:\n",
    "\n",
    "{tools}\n",
    "\n",
    "Use the following format:\n",
    "\n",
    "Question: the input question you must answer\n",
    "Thought: you should always think about what to do\n",
    "Action: the action to take, should be one of [{tool_names}]\n",
    "Action Input: the input to the action\n",
    "Observation: the result of the action\n",
    "... (this Thought/Action/Action Input/Observation can repeat N times)\n",
    "Thought: I now know the final answer\n",
    "Final Answer: the final answer to the original input question\n",
    "\n",
    "Begin!\n",
    "\n",
    "Question: {input}\n",
    "Thought:{agent_scratchpad}'\n",
    "```\n",
    "\n",
    "在上述prompt中，大模型根据输入的问题{question}，来构造了一个思维链。在这个思维链中：问题、思考、行为、行动的输入、行动的结果，这五点是Agent工作的核心。在思考中，我们定义了Agent的scratchpad，这个scratchpad可以用于记录Agent的思考过程。在行动中，我们定义了Agent的工具{tools}，这个工具可以用于帮助Agent解决问题。这个思考过程可以进行N次直到Agent认为自己已经解决了问题。这时通过Thought: I now know the final answer来结束这个思考过程，然后通过Final Answer来输出最终的答案。\n",
    "\n",
    "理解了上述过程，我们就可以考虑如何实现Agent的工具。\n",
    "\n",
    "![image.png](Agent结构.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4.自定义大模型XML交互工具\n",
    "\n",
    "### 4.1.定义XML交互格式\n",
    "\n",
    "基于上述Langchain的思路我们来研究如何自定义一个工具。为了便于和大模型交互，我们定义一个XML交互工具。当用户向大模型提问时，大模型必须返回XML格式的回复，便于我们后续解析和调用工具。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<final_answer>很抱歉，作为当前知识库的一部分，我无法找到关于“灵积”这项具体服务的相关信息。它可能是一个较为冷门、新兴的服务，或者是我所访问的信息源中尚未涵盖的内容。如果您能提供更详细的背景信息或上下文，我或许能够更有效地帮助您。另外，如果“灵积”是您所在企业或特定行业内部使用的专属服务，请您直接咨询相关团队或查阅内部文档以获取准确信息。如果您希望我继续尝试使用“find_service”工具进行查询，请告知，但请注意，由于该工具主要适用于查询企业内部服务信息，对于未知的外部服务或公众不普遍知晓的服务，其查询结果可能仍然有限。</final_answer>\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.llms import Tongyi\n",
    "\n",
    "llm = Tongyi()\n",
    "# 使用更强大的通义千问 max\n",
    "llm.model_name = 'qwen-max'\n",
    "\n",
    "prompt = '''你是一个可以回答任何问题的助手。\n",
    "你可以使用下列工具: \n",
    "\n",
    "find_service: 当你不确定答案时，你可以使用此工具。\n",
    "\n",
    "为了使用这个工具，你必须用<tool></tool>和<tool_input></tool_input>标签。\n",
    "-----\n",
    "例如，如果您有一个名为“find_service”的工具，可以查询企业内部服务信息，\n",
    "question: 阿里云是什么服务\n",
    "completion: <tool>find_service</tool><tool_input>阿里云</tool_input>\n",
    "\n",
    "使用工具后你会得到一个形式为<observation></observation>的响应，因此在第二轮你会得到输入为\n",
    "<tool>find_service</tool><tool_input>阿里云</tool_input><observation>阿里云服务简介...</observation>\n",
    "\n",
    "你需要判断<observation></observation>标签中的内容是不是你需要的答案。如果是最终答案final_answer，你需要返回答案：\n",
    "<final_answer>阿里云服务简介...</final_answer>\n",
    "\n",
    "如果你不需要使用工具也能回答问题，你也必须要用<final_answer></final_answer>标签来包裹答案。比如\n",
    "<final_answer>阿里云服务详细介绍...</final_answer>\n",
    "\n",
    "\n",
    "开始任务：\n",
    "\n",
    "问题：\n",
    "'''\n",
    "\n",
    "print(llm.invoke(prompt + '灵积是什么服务'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.2.定义XML解析函数和搜索工具"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'DashScope灵积模型服务建立在“模型即服务”（Model-as-a-Service，MaaS）的理念基础之上，围绕AI各领域模型，通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。\\nDashScope灵积模型服务依托于业界各领域的优质模型，基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅！'"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.output_parsers import XMLOutputParser\n",
    "\n",
    "# 从xml中获取tool和tool_input\n",
    "def get_tool_and_input(response):\n",
    "    xml_response = f\"<xml>{response}</xml>\"\n",
    "    parsed_xml_response = XMLOutputParser().invoke(xml_response)\n",
    "    response_dict = {}\n",
    "    for item in parsed_xml_response['xml']:\n",
    "        for key in item:\n",
    "            response_dict[key] = item[key]\n",
    "    return response_dict\n",
    "\n",
    "# 运行tool\n",
    "def run_tool(response_dict):\n",
    "    search_input =''\n",
    "    search_result = ''\n",
    "    if response_dict.get('tool') is None:\n",
    "        search_result = response\n",
    "    elif response_dict['tool'] == 'find_service':\n",
    "        search_input = response_dict['tool_input']\n",
    "        search_result = SearchTool().run(search_input)\n",
    "    return search_input, search_result\n",
    "\n",
    "response=\"\"\"<tool>find_service</tool><tool_input>灵积</tool_input>\"\"\"\n",
    "response_dict = get_tool_and_input(response)\n",
    "search_input, search_result = run_tool(response_dict)\n",
    "search_result\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.3.一个完整的工具定义\n",
    "\n",
    "为了更好帮助读者理解大模型Agent调用的原理，下面的代码是我们自己定义了一个简单的工具类 SearchTool 类，而不是继承自langchain的 BaseTool。并且我们实现了一个简单的 DIYAgent 类，这个类参考了langchain的接口，实现基本的调用工具的能力。这样读者可以看到大模型是如何理解用户问题并返回调用工具的指令，而Agent又是如何理解这个指令，并启动对应工具的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始响应： <tool>SearchTool</tool><tool_input>灵积</tool_input>\n",
      "使用工具： SearchTool\n",
      "查询内容： 灵积\n",
      "搜索结果： DashScope灵积模型服务建立在“模型即服务”（Model-as-a-Service，MaaS）的理念基础之上，围绕AI各领域模型，通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型，基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅！\n",
      "最终输出： DashScope灵积模型服务是一种基于“模型即服务”（Model-as-a-Service，MaaS）理念构建的服务平台，专注于提供人工智能各领域的模型服务。该服务以标准化API接口的形式，支持模型推理与模型微调训练等多样化功能。其核心优势在于整合并利用了行业内的优质模型资源，且依托于阿里巴巴集团强大的云计算基础设施。对于AI应用开发者而言，DashScope灵积模型服务旨在成为他们便捷获取、使用及定制模型的理想之选，助力他们在AI开发过程中快速推进模型探索与应用实践。\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.llms import Tongyi\n",
    "import json\n",
    "\n",
    "# 定义模型\n",
    "llm = Tongyi()\n",
    "llm.model_name = 'qwen-max'\n",
    "\n",
    "# 定义搜索工具\n",
    "class SearchTool():\n",
    "    \"\"\"服务查询工具\"\"\"\n",
    "\n",
    "    name: str = \"服务查询工具\"\n",
    "    description: str = (\n",
    "        \"当你不确定一个服务是什么，才使用此工具。\"\n",
    "    )\n",
    "\n",
    "    def run(self, name: str) -> str:\n",
    "        if name == '灵积':\n",
    "            return '''DashScope灵积模型服务建立在“模型即服务”（Model-as-a-Service，MaaS）的理念基础之上，围绕AI各领域模型，通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型，基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅！'''\n",
    "        return name + '抱歉，没有查到相关信息。'\n",
    "    \n",
    "\n",
    "# 定义查询模版\n",
    "prompt_template = '''你是一个可以回答任何问题的助手。\n",
    "你可以使用下列工具: \n",
    "\n",
    "{tools}: 当你不确定答案时，你可以使用此工具。\n",
    "\n",
    "为了使用这个工具，你必须用<tool></tool>和<tool_input></tool_input>标签。\n",
    "例如，如果您有一个名为“{tool1}”的工具，可以查询企业内部服务信息，为了搜索(阿里云)是什么服务，你会返回：\n",
    "<tool>{tool1}</tool><tool_input>阿里云</tool_input>\n",
    "\n",
    "使用工具后你会得到一个形式为<observation></observation>的响应，因此在第二轮你会得到输入为\n",
    "<tool>{tool1}</tool><tool_input>阿里云</tool_input><observation>阿里云服务简介XXX</observation>\n",
    "\n",
    "你需要判断<observation></observation>标签中的内容是不是你需要的答案。如果是最终答案final_answer，你需要返回答案：\n",
    "<final_answer>阿里云服务简介XXX</final_answer>\n",
    "\n",
    "如果你不需要使用工具也能回答问题，你也必须要用<final_answer></final_answer>标签来包裹答案。比如\n",
    "<final_answer>阿里云服务详细介绍XXX</final_answer>\n",
    "\n",
    "开始任务：\n",
    "\n",
    "问题：\n",
    "'''\n",
    "\n",
    "\n",
    "# 定义一个简单的工具调用Agent\n",
    "class DIYAgent():\n",
    "    def __init__(self,tool,model=None,prompt=None):\n",
    "        self.tool = tool\n",
    "        self.model = model\n",
    "        self.verbose = False\n",
    "        self.tool_name = self.get_class_name(self.tool) \n",
    "        self.prompt = prompt.format(tools=self.tool_name,tool1 = self.tool_name)\n",
    "\n",
    "    def get_class_name(self,tool):\n",
    "        return tool.__class__.__name__\n",
    "    \n",
    "    # 从xml中获取tool和tool_input\n",
    "    def get_tool_and_input(self, response):\n",
    "        xml_response = f\"<xml>{response}</xml>\"\n",
    "        parsed_xml_response = XMLOutputParser().invoke(xml_response)\n",
    "        response_dict = {}\n",
    "        for item in parsed_xml_response['xml']:\n",
    "            for key in item:\n",
    "                response_dict[key] = item[key]\n",
    "        return response_dict\n",
    "\n",
    "    # 执行工具\n",
    "    def run_tool(self, response_dict):\n",
    "        search_input =''\n",
    "        search_result = ''\n",
    "\n",
    "        if response_dict.get('tool') is None:\n",
    "            if response_dict.get('final_answer') is not None:\n",
    "                return search_input, response_dict['final_answer']\n",
    "            else:\n",
    "                return search_input, \"Error in tool\"+json.dumps(response_dict)\n",
    "\n",
    "        if response_dict['tool'] ==  self.tool_name:\n",
    "            search_input = response_dict['tool_input']\n",
    "            search_result = self.tool.run(search_input)\n",
    "            return search_input, search_result\n",
    "\n",
    "    # run agent\n",
    "    def run(self, request):        \n",
    "        raw_response = self.model.invoke(self.prompt + request)\n",
    "        print(\"原始响应：\", raw_response)\n",
    "        response_dict = self.get_tool_and_input(raw_response)\n",
    "        print(\"使用工具：\", response_dict['tool'])\n",
    "        print(\"查询内容：\", response_dict['tool_input'])\n",
    "        search_input, search_result = self.run_tool(response_dict)\n",
    "        print(\"搜索结果：\", search_result)\n",
    "\n",
    "        new_prompt = self.prompt + f\"<tool>{self.tool_name}</tool><tool_input>{search_input}</tool_input><observation>{search_result}</observation>\"\n",
    "        final_response = self.model.invoke(new_prompt)\n",
    "        final_response_dict = self.get_tool_and_input(final_response)\n",
    "        search_input, final_answer = self.run_tool(final_response_dict)\n",
    "        return final_answer\n",
    "\n",
    "\n",
    "# MIAN Function\n",
    "tool = SearchTool()\n",
    "\n",
    "diyAgent = DIYAgent(tool ,llm, prompt_template)\n",
    "\n",
    "result = diyAgent.run(\"灵积是什么服务\")\n",
    "print(\"最终输出：\",result)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.4.添加更多的搜索词条\n",
    "\n",
    "扩展搜索范围时，我们只需要扩展SearchTool函数的能力即可，比如从搜索引擎上下载信息。为了进行对比，我们直接填入一些已知信息。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始响应： <tool>SearchTool2</tool><tool_input>百炼</tool_input>\n",
      "使用工具： SearchTool2\n",
      "查询内容： 百炼\n",
      "搜索结果： 百炼，即大模型服务平台，是面向企业客户及合作伙伴的，基于通义大模型、行业大模型以及三方大模型，结合企业专属数据，包含全链路大模型开发工具的一站式大模型商业化平台。提供完整的模型训练、微调、评估等产品工具，预置丰富的应用插件，提供便捷的集成方式，更快更高效地完成大模型应用的构建。\n",
      "最终输出： 百炼是面向企业客户及合作伙伴的大模型服务平台，基于通义大模型、行业大模型以及三方大模型，结合企业专属数据，提供全链路大模型开发工具，包括模型训练、微调、评估等产品工具。该平台预置丰富应用插件，支持便捷的集成方式，旨在帮助用户快速高效地构建大模型应用。\n"
     ]
    }
   ],
   "source": [
    "# 扩展支持的服务查询\n",
    "class SearchTool2():\n",
    "    \"\"\"服务查询工具\"\"\"\n",
    "\n",
    "    name: str = \"服务查询工具\"\n",
    "    description: str = (\n",
    "        \"当你不确定一个服务是什么，才使用此工具。\"\n",
    "    )\n",
    "\n",
    "    def run(self, name: str) -> str:\n",
    "        if name == '灵积':\n",
    "            return '''DashScope灵积模型服务建立在“模型即服务”（Model-as-a-Service，MaaS）的理念基础之上，围绕AI各领域模型，通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型，基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅！'''\n",
    "        elif name==\"百炼\":\n",
    "            return \"百炼，即大模型服务平台，是面向企业客户及合作伙伴的，基于通义大模型、行业大模型以及三方大模型，结合企业专属数据，包含全链路大模型开发工具的一站式大模型商业化平台。提供完整的模型训练、微调、评估等产品工具，预置丰富的应用插件，提供便捷的集成方式，更快更高效地完成大模型应用的构建。\"\n",
    "        elif (name==\"PAI\") or (name==\"阿里云 PAI\"):\n",
    "            return \"人工智能平台 PAI（Platform of Artificial Intelligence）面向企业客户及开发者，提供轻量化、高性价比的云原生人工智能，涵盖DSW交互式建模、Designer拖拽式可视化建模、DLC分布式训练到EAS模型在线部署的全流程。\"\n",
    "        elif name==\"OSS\":\n",
    "            return \"阿里云对象存储OSS（Object Storage Service）是一款海量、安全、低成本、高可靠的云存储服务，可提供99.9999999999%（12个9）的数据持久性，99.995%的数据可用性。多种存储类型供选择，全面优化存储成本。\"\n",
    "        return name + '抱歉，没有查到相关信息。'\n",
    "    \n",
    "\n",
    "# MIAN Function\n",
    "tool = SearchTool2()\n",
    "\n",
    "diyAgent = DIYAgent(tool ,llm, prompt_template)\n",
    "\n",
    "result = diyAgent.run(\"百炼是什么服务\")\n",
    "print(\"最终输出：\",result)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.5.使用封装好的代码来实现服务查询\n",
    "\n",
    "我们将上述代码重构后封装在文件[`search-with-tool.py`](search-with-tool.py)中。为了便于后续章节扩展，我们实现了列表式工具加载，与langchain的接口保持一致。\n",
    "  \n",
    "列表式工具加载：```tool = [SearchTool()]```\n",
    "\n",
    "你可以尝试用自己的方式来修改代码。这段代码可以用以下方式来运行：\n",
    "\n",
    "```bash\n",
    "python3 search-with-tool.py -question=\"the question\"\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m正在分析内容...\u001b[0m\n",
      "\u001b[32m使用工具：SearchTool...\u001b[0m\n",
      "\u001b[32m查询内容：百炼...\u001b[0m\n",
      "\u001b[32m查询到参考信息：百炼，即大模型服务平台，是面向企业客户及合作伙伴的，基于通义...\u001b[0m\n",
      "\u001b[32m正在分析内容...\u001b[0m\n",
      "\u001b[32m最终答案：百炼是面向企业客户及合作伙伴的大模型服务平台，基于通义大模型、行业大模型以及三方大模型，结合企业专属数据，具备全链路大模型开发工具，支持一站式大模型商业化应用。平台提供模型训练、微调、评估等全套产品工具，内置丰富应用插件，并以便捷的集成方式助力用户快速高效构建大模型应用。\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "! python search-with-tool.py --question=\"百炼是什么服务\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32m正在分析内容...\u001b[0m\n",
      "\u001b[32m使用工具：SearchTool...\u001b[0m\n",
      "\u001b[32m查询内容：PAI...\u001b[0m\n",
      "\u001b[32m查询到参考信息：人工智能平台 PAI（Platform of Artific...\u001b[0m\n",
      "\u001b[32m正在分析内容...\u001b[0m\n",
      "\u001b[32m最终答案：PAI（Platform of Artificial Intelligence）是面向企业客户及开发者的人工智能平台，它提供轻量化、高性价比的云原生人工智能服务。PAI覆盖了从DSW交互式建模、Designer拖拽式可视化建模、DLC分布式训练到EAS模型在线部署的全流程。\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "! python search-with-tool.py --question=\"PAI是什么服务\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. 总结\n",
    "本章我们首先用LangChain的的接口定义了一个Agent应用，为了理解大模型使用Agent的内核，我们用自己的代码实现了一遍。这段代码包括工具定义类prompt定义模版，工具调用，工具查询等功能。从这里我们看到，LangChain方法主要思路是通过详细定义的Prompt让大模型反复思考“大模型目前获得的信息是否可以回答用户的问题”，如果不能解答用户的问题，大模型应该使用何种工具；如果可以解答用户的问题，那么就输出结果。\n",
    "基于以上分析，为了实现更强大的大模型助理的能力，我们还可能需要为大模型加载更多功能。比如加入一些记忆能力让大模型可以把查询结果、历史对话、历史运行结果进行缓存，这样大模型有可能知道他的目标是什么，现在是什么进度，接下来可以怎么做。这样我们还需要为大模型加入规划的能力，大模型就可以在某一个阶段里，更好的规划下一步行动的计划。我们还可以为大模型加入更多外界反馈，甚至加入机械手和视觉系统，这样大模型就可能做到指导一台机器人去为人类打一杯咖啡。但是，接下来我们想先走一小步，让大模型写代码。\n",
    "\n",
    "## 6. 参考资料\n",
    "- [DashScope](https://dashscope.aliyun.com/)\n",
    "- [LangChain](https://python.langchain.com/docs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "chatllm",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
